package wangxiaotao.shiro.config;

import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.mgt.*;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.Cookie;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import wangxiaotao.shiro.cache.RedisCacheManager;
import wangxiaotao.shiro.common.RedisManager;
import wangxiaotao.shiro.filter.CustomFilter;
import wangxiaotao.shiro.realms.CustomRealm;

import javax.servlet.Filter;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

@Configuration
public class ShiroConfig {

    /**
     * 过滤配置
     * @param securityManager
     * @return
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager){
        ShiroFilterFactoryBean filterFactoryBean = new ShiroFilterFactoryBean();
        //设置filter名称 对应的过滤器
        Map<String , Filter> filters = new HashMap<>();
        filters.put("filter",new CustomFilter());
        filterFactoryBean.setFilters(filters);
        //设置安全管理器
        filterFactoryBean.setSecurityManager(securityManager);
        Map<String,String > filterMap = new LinkedHashMap<>();
        filterMap.put("/css/**","anon");//请求放行
        filterMap.put("/fonts/**","anon");//请求放行
        filterMap.put("/img/**","anon");//请求放行
        filterMap.put("/js/**","anon");//请求放行
        filterMap.put("/favicon.ico","anon");//请求放行
        filterMap.put("/index.html","anon");//请求放行
        filterMap.put("/","anon");//请求放行

        filterMap.put("/common/**","anon");//请求放行
        filterMap.put("/websocket/**","anon");//请求放行
        filterMap.put("/pay/**","anon");//请求放行

        filterMap.put("/**","filter");//拦截所有请求,交给filter处理
        //执行链设置
        filterFactoryBean.setFilterChainDefinitionMap(filterMap);

        return filterFactoryBean;
    }

    @Bean
    public SubjectFactory subjectFactory(){
        return new StatelessDefaultSubjectFactory();
    }

    /**
     * 配置核心安全事务管理器
     * @return
     */
    @Bean
    public SecurityManager securityManager(Realm realm) {
        DefaultWebSecurityManager securityManager =  new DefaultWebSecurityManager();
        //设置自定义realm
        securityManager.setRealm(realm);
        //配置redis缓存
        securityManager.setCacheManager(redisCacheManager());
        //配置记住我
//        securityManager.setRememberMeManager(rememberMeManager());
        //配置redis缓存
        securityManager.setCacheManager(redisCacheManager());
        //配置自定义session管理，使用redis
        securityManager.setSessionManager(sessionManager());

        //关闭session
        DefaultSubjectDAO defaultSubjectDAO = new DefaultSubjectDAO();
        defaultSubjectDAO.setSessionStorageEvaluator(sessionStorageEvaluator());
        securityManager.setSubjectDAO(defaultSubjectDAO);

        return securityManager;
    }


    /**
     * 注入SessionStorageEvaluator,关闭Session存储
     */
    @Bean
    public SessionStorageEvaluator sessionStorageEvaluator() {
        DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator();
        defaultSessionStorageEvaluator.setSessionStorageEnabled(false);
        return defaultSessionStorageEvaluator;
    }
    @Bean
    public SessionManager sessionManager() {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        sessionManager.setSessionIdCookie(sessionIdCookie());
        //是否开启定时调度器进行检测过期session 默认为true
        sessionManager.setSessionValidationSchedulerEnabled(false);
        return sessionManager;
    }

    /**
     * 配置保存sessionId的cookie
     * 注意：这里的cookie 不是上面的记住我 cookie 记住我需要一个cookie session管理 也需要自己的cookie
     * 默认为: JSESSIONID 问题: 与SERVLET容器名冲突,重新定义为sid
     * @return
     */
    @Bean
    public SimpleCookie sessionIdCookie(){
        //这个参数是cookie的名称
        SimpleCookie simpleCookie = new SimpleCookie("sid");
        //set-cookie的httponly属性如果设为true的话，会增加对xss防护的安全系数。它有以下特点：
        //设为true后，只能通过http访问，javascript无法访问
        simpleCookie.setHttpOnly(true);
        simpleCookie.setPath("/");
        simpleCookie.setSameSite(Cookie.SameSiteOptions.NONE);
        simpleCookie.setMaxAge(-1);
        return simpleCookie;
    }

    /**
     * 自定义验证realm
     * @return
     */
    @Bean
    public CustomRealm customRealm(){
        CustomRealm customRealm = new CustomRealm();
        customRealm.setCachingEnabled(true);
        //启用身份验证缓存，即缓存AuthenticationInfo信息，默认false
        customRealm.setAuthenticationCachingEnabled(true);
        //缓存AuthenticationInfo信息的缓存名称
        customRealm.setAuthenticationCacheName("authenticationCache");
        //启用授权缓存，即缓存AuthorizationInfo信息，默认false
        customRealm.setAuthorizationCachingEnabled(true);
        //缓存AuthorizationInfo信息的缓存名称
        customRealm.setAuthorizationCacheName("authorizationCache");
        return customRealm;
    }

    /**
     * shiro缓存管理器;  需要添加到securityManager中
     * @return RedisCacheManager
     */
    @Bean
    public RedisCacheManager redisCacheManager(){
        RedisCacheManager redisCacheManager = new RedisCacheManager();
        redisCacheManager.setRedisManager(redisManager());
        //redis中针对不同用户缓存 识别不同用户用token里面的荷载 修改请注意荷载存在 和唯一性
//        redisCacheManager.setPrincipalIdFieldName("id");
        //用户权限信息缓存时间 seconds 默认1800seconds
        redisCacheManager.setExpire(3600);
        return redisCacheManager;
    }

    @Bean
    public RedisManager redisManager(){
        return new RedisManager();
    }

    /**
     * cookie对象;会话Cookie模板 ,默认为: JSESSIONID 问题: 与SERVLET容器名冲突,重新定义为sid或rememberMe，自定义
     * @return
     */
//    @Bean
//    public SimpleCookie rememberMeCookie(){
//        //这个参数是cookie的名称，对应前端的checkbox的name = rememberMe
//        SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
//        //setcookie的httponly属性如果设为true的话，会增加对xss防护的安全系数。它有以下特点：
//        //设为true后，只能通过http访问，javascript无法访问
//        //防止xss读取cookie
//        simpleCookie.setHttpOnly(true);
//        simpleCookie.setPath("/");
//        //记住我cookie生效时间 ,单位秒
//        simpleCookie.setMaxAge(3600);
//        simpleCookie.setSameSite(Cookie.SameSiteOptions.NONE);
//        return simpleCookie;
//    }
    /**
     * cookie管理对象;记住我功能,rememberMe管理器
     * @return
     */
//    @Bean
//    public CookieRememberMeManager rememberMeManager(){
//        CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
//        cookieRememberMeManager.setCookie(rememberMeCookie());
//        //rememberMe cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度(128 256 512 位)
//        cookieRememberMeManager.setCipherKey(Base64.decode("4AvVhmFLUs0KTA3Postag="));
//        return cookieRememberMeManager;
//    }

    /**
     * FormAuthenticationFilter 过滤器 过滤记住我
     * @return
     */
//    @Bean
//    public FormAuthenticationFilter formAuthenticationFilter(){
//        FormAuthenticationFilter formAuthenticationFilter = new FormAuthenticationFilter();
//        //对应前端的checkbox的name = rememberMe
//        formAuthenticationFilter.setRememberMeParam("rememberMe");
//        return formAuthenticationFilter;
//    }
    /**
     * 配置session监听
     * @return ShiroSessionListener
     */
//    @Bean
//    public ShiroSessionListener sessionListener(){
//        return new ShiroSessionListener();
//    }

    /**
     * 配置会话ID生成器
     * @return ShiroSessionIdGenerator
     */
//    @Bean
//    public SessionIdGenerator sessionIdGenerator() {
//        return new ShiroSessionIdGenerator();
//    }

    /**
     * SessionDAO的作用是为Session提供CRUD并进行持久化的一个shiro组件
     * @return SessionDAO
     */
//    @Bean
//    public SessionDAO sessionDAO() {
//        RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
//        redisSessionDAO.setRedisManager(redisManager());
//        //配置session id 生成
//        redisSessionDAO.setSessionIdGenerator(sessionIdGenerator());
//        //session会话在redis中的保存时间,最好大于session会话超时时间
//        //Please make sure expire is longer than sesion.getTimeout()
//        redisSessionDAO.setExpire(3600);
//        return redisSessionDAO;
//    }

    /**
     * 自定义sssion
     * @return ShiroSession
     */
//    @Bean
//    public ShiroSession session(){
//        return new ShiroSession();
//    }

    /**
     *  创建session的工厂
     * @return ShiroSessionFactory
     */
//    @Bean
//    public ShiroSessionFactory sessionFactory(){
//        return new ShiroSessionFactory();
//    }

    /**
     * 配置会话管理器，设定会话超时及保存
     * @return
     */
//    @Bean
//    public SessionManager sessionManager() {
//        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
//        Collection<SessionListener> listeners = new ArrayList<>();
//        //配置监听
//        listeners.add(sessionListener());
//        sessionManager.setSessionListeners(listeners);
//        //设置set-cookie的格式
//        sessionManager.setSessionIdCookie(sessionIdCookie());
//        //session crud
//        sessionManager.setSessionDAO(sessionDAO());
//        sessionManager.setCacheManager(redisCacheManager());
//        //session工厂
////        sessionManager.setSessionFactory(sessionFactory());
//
//        //全局会话超时时间（单位毫秒），默认30分钟
//        sessionManager.setGlobalSessionTimeout(1000*60*60);
//        //是否开启删除无效的session对象  默认为true
//        sessionManager.setDeleteInvalidSessions(true);
//        //是否开启定时调度器进行检测过期session 默认为true
//        sessionManager.setSessionValidationSchedulerEnabled(true);
//        //设置session失效的扫描时间, 清理用户直接关闭浏览器造成的孤立会话 默认为 1个小时
//        //这里会清理redis里面的session缓存
//        sessionManager.setSessionValidationInterval(1000*60*60);
//        //url 后面的 JSESSIONID
////        sessionManager.setSessionIdUrlRewritingEnabled(true);
//        return sessionManager;
//    }

    /**
     * 开启aop注解支持
     * @param securityManager
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }
    /**
     * 配置Shiro生命周期处理器
     * @return
     */
    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }
}
